#pragma once

namespace xBase
{
    class CRefCountedBase
    {
    protected:
        VOID AddRef();

        // Returns true if the object should self-delete.
        BOOL Release();

        CRefCountedBase();
        ~CRefCountedBase();

    private:
        INT m_nRefCount;

    private:
        CRefCountedBase(const CRefCountedBase & right);
        CRefCountedBase & operator =(const CRefCountedBase & right);
    };

    class CRefCountedThreadSafeBase
    {
    protected:
        CRefCountedThreadSafeBase();
        virtual ~CRefCountedThreadSafeBase();

        VOID AddRef();

        // Returns true if the object should self-delete.
        BOOL Release();

    private:
        INT m_nRefCount;

    private:
        CRefCountedThreadSafeBase(const CRefCountedThreadSafeBase & right);
        CRefCountedThreadSafeBase & operator =(const CRefCountedThreadSafeBase & right);
    };

    template<class T>
    class CRefCounted : public CRefCountedBase
    {
    public:
        CRefCounted(VOID);
        virtual ~CRefCounted(VOID);

        VOID AddRef() {
            CRefCountedBase::AddRef();
        }

        VOID Release() {
            if (CRefCountedBase::Release()) {
                delete static_cast<T*>(this);
            }
        }

    private:
        CRefCounted(const CRefCounted<T> & right);
        CRefCounted<T> & operator = (const CRefCounted<T> & right);
    };

    template <class T>
    class CRefCountedThreadSafe : public CRefCountedThreadSafeBase 
    {
    public:
        CRefCountedThreadSafe() { }
        ~CRefCountedThreadSafe() { }

        virtual VOID AddRef() 
        {
            CRefCountedThreadSafeBase::AddRef();
        }

        virtual VOID Release() 
        {
            if (CRefCountedThreadSafeBase::Release()) 
            {
                delete static_cast<T*>(this);
            }
        }

    private:
        CRefCountedThreadSafe(const CRefCountedThreadSafe<T> & right);
        CRefCountedThreadSafe<T> & operator = (const CRefCountedThreadSafe<T> & right);
    };

#define NON_VIRTUAL
    class CFakeRefCounted
    {
    public:
        NON_VIRTUAL VOID AddRef(){};
        NON_VIRTUAL VOID Release(){};
        static BOOL ImplementsThreadSafeReferenceCounting() {return TRUE;}
    };

    template <class T>
    class CScopedRefPtr {
    public:
        CScopedRefPtr() : m_ptr(NULL) {
        }

        CScopedRefPtr(T* p) : m_ptr(p) {
            if (m_ptr)
                m_ptr->AddRef();
        }

        CScopedRefPtr(const CScopedRefPtr<T>& r) : m_ptr(r.m_ptr) {
            if (m_ptr)
                m_ptr->AddRef();
        }

        template <typename U>
        CScopedRefPtr(const CScopedRefPtr<U>& r) : m_ptr(r.get()) {
            if (m_ptr)
                m_ptr->AddRef();
        }

        ~CScopedRefPtr() {
            if (m_ptr)
                m_ptr->Release();
        }

        T* get() const { return m_ptr; }
        operator T*() const { return m_ptr; }
        T* operator->() const { return m_ptr; }

        // 	T** operator&()
        // 	{
        // 		ATLASSERT(m_ptr==NULL);
        // 		return &m_ptr;
        // 	}

        // Release a pointer.
        // The return value is the current pointer held by this object.
        // If this object holds a NULL pointer, the return value is NULL.
        // After this operation, this object will hold a NULL pointer,
        // and will not own the object any more.
        T* release() {
            T* retVal = m_ptr;
            m_ptr = NULL;
            return retVal;
        }

        CScopedRefPtr<T>& operator=(T* p) {
            // AddRef first so that self assignment should work
            if (p)
                p->AddRef();
            if (m_ptr )
                m_ptr ->Release();
            m_ptr = p;
            return *this;
        }

        VOID Reset(T* p = NULL)
        {
            if(p != m_ptr)
            {
                enum { TypeMustBeComplete = sizeof(T) };

                if(NULL != m_ptr)
                {
                    m_ptr->Release();
                }

                m_ptr = p;
            }
        }

        CScopedRefPtr<T>& operator=(const CScopedRefPtr<T>& r) {
            return *this = r.m_ptr;
        }

        template <typename U>
        CScopedRefPtr<T>& operator=(const CScopedRefPtr<U>& r) {
            return *this = r.get();
        }

        VOID swap(T** pp) {
            T* p = m_ptr;
            m_ptr = *pp;
            *pp = p;
        }

        VOID swap(CScopedRefPtr<T>& r) {
            swap(&r.m_ptr);
        }

        HRESULT CreateInstance()
        {
            ATLASSERT(m_ptr == NULL);
            HRESULT hr = T::CreateInstance(&m_ptr);
            if (SUCCEEDED(hr) && m_ptr != NULL)
                m_ptr->AddRef();
            return hr;
        }

    public:
        T* m_ptr;
    };

    template <typename T>
    CScopedRefPtr<T> make_scoped_refptr(T* t) {
        return CScopedRefPtr<T>(t);
    }

    template<class T>
    CRefCounted<T>::CRefCounted(VOID)
    {
    }

    template<class T>
    CRefCounted<T>::~CRefCounted(VOID)
    {
    }
}

namespace xBase
{

    inline VOID CRefCountedBase::AddRef()
    {
        ++ m_nRefCount;
    }

    inline BOOL CRefCountedBase::Release()
    {
        if (0 == --m_nRefCount)
            return TRUE;
        else
            return FALSE;
    }

    inline CRefCountedBase::CRefCountedBase()
        : m_nRefCount(0)
    {
    }

    inline CRefCountedBase::~CRefCountedBase()
    {

    }

    inline CRefCountedThreadSafeBase::CRefCountedThreadSafeBase()
        : m_nRefCount(0)
    {

    }

    inline CRefCountedThreadSafeBase::~CRefCountedThreadSafeBase()
    {

    }

    inline VOID CRefCountedThreadSafeBase::AddRef()
    {
        InterlockedExchangeAdd(reinterpret_cast<volatile LONG*>(&m_nRefCount), 1);
    }

    // Returns true if the object should self-delete.
    inline BOOL CRefCountedThreadSafeBase::Release()
    {
        LONG nNewValue = InterlockedExchangeAdd(
            reinterpret_cast<volatile LONG*>(&m_nRefCount), -1) - 1;

        if (0 == nNewValue)
            return TRUE;
        else
            return FALSE;
    }

}